package junior.util.address;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Address implements Comparable<Address>, Serializable{
	
	private static final long serialVersionUID = 1l;

	private final double score;
	
	private final List<AddressToken> tokens;
	
	private final Map<AddressTokenLevel, List<AddressToken>> levelMap;
	
	Address(){
		this.tokens = Collections.emptyList();
		this.score = 0;
		this.levelMap = Collections.emptyMap();
	}
	
	public Address(List<AddressToken> tokens){
		this.tokens = tokens;
		this.levelMap = new EnumMap<AddressTokenLevel, List<AddressToken>>(AddressTokenLevel.class);
		double s = 0;
		for(AddressToken token: tokens){
			s += token.getLevel().getLengthProb(token.getPrimary().length());
			List<AddressToken> list = levelMap.get(token.getLevel());
			if(list == null){
				list = new ArrayList<AddressToken>(4);
				levelMap.put(token.getLevel(), list);
			}
			list.add(token);
		}
		this.score = s;
	}

	public double getScore() {
		return score;
	}

	public List<AddressToken> getTokens() {
		return tokens;
	}
	
	/**
	 * 按照顺序根据类型获取tokens
	 * 
	 * @param levels 需要返回的token的词性
	 * @return 如果为null表示没有，否则是长度和参数相同的token数组
	 * @author ZhangXiaoye
	 * @date 2016年11月19日 下午4:30:01
	 */
	public AddressToken[] getTokensOfLevel(AddressTokenLevel... levels){
		for(int i= levels.length - 1; i < tokens.size(); i ++){
			List<AddressToken> sub = tokens.subList(i - levels.length + 1, i + 1);
			boolean match = true;
			for(int j = 0; j < levels.length; j ++){
				if(! sub.get(j).getLevel().equals(levels[j])){
					match = false;
					break;
				}
			}
			if(match){
				return sub.toArray(new AddressToken[0]);
			}
		}
		return null;
	}
	
	public double similarity(Address o, AddressIndex<?> index){
		
		boolean hasNonFuzzy = false;
		double s = 0;
		for(AddressTokenLevel l: AddressTokenLevel.values()){
			List<AddressToken> a = levelMap.get(l);
			List<AddressToken> b = o.levelMap.get(l);
			if((a != null && ! a.isEmpty()) && (b != null && ! b.isEmpty())){
				Set<AddressToken> union = new HashSet<AddressToken>(a);
				union.retainAll(b);
				if(union.isEmpty()){
					// 相同层级都有但是不匹配
					return 0;
				}
				Integer levelCount = index.levelCount.get(l);
				if(levelCount == null){
					levelCount = 1;
				}
				for(AddressToken token: union){
					Set<Integer> docSet = index.indexMap.get(token);
					double docFreq = docSet != null? docSet.size() + 1: 1;
					s += Math.log(1 + levelCount / docFreq);
				}
				if(! AddressTokenLevel.isFuzzy(l)){
					hasNonFuzzy = true;
				}
			}
		}
		return hasNonFuzzy? s: 0;
	}
	
	public Map<AddressTokenLevel, List<AddressToken>> getLevelMap(){
		return levelMap;
	}

	@Override
	public int compareTo(Address o) {
		return Double.compare(o.score, this.score);
	}

}
